package org.balthie.demo.jdk.nio.channel.file;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

// 
// 

/**
 * @author：balthie@126.com
 * @createtime ： 2023年1月5日 下午2:44:41
 * @description <a href=
 *              "https://cloud.tencent.com/developer/article/2032919">两个Java进程map文件映射到内存，这样两个进程就可以通过这个共享内存区域实现进程间到通信了</a>
 * 
 *              <a href=
 *              "http://events.jianshu.io/p/8050fcc47fee">ByteBuffer学习</a>
 *              position：当前的下标位置，表示进行下一个读写操作时的起始位置；
 *              limit：结束标记下标，表示进行下一个读写操作时的（最大）结束位置；
 *              capacity：该ByteBuffer容量；
 *
 * @since version 初始于版本 TODO
 */

public class FileChannelExchage
{
    private static final String TARGET_USER = "target/user";
    
    private static final String TARGET_USER_USERHELLO_TXT = TARGET_USER + "/userhello.txt";
    
    private static final Logger LOGGER = LoggerFactory.getLogger(FileChannelExchage.class);
    
    public static void main(String args[]) throws InterruptedException, IOException
    {
        createIfNoexist();
        
        System.out.println(Charset.defaultCharset());
        
        new Thread(new Asker()).start();
        
        new Thread(new Answer()).start();
        
        Thread.sleep(10000);
    }
    
    static class Asker implements Runnable
    {
        public void run()
        {
            RandomAccessFile f = null;
            try
            {
                f = new RandomAccessFile(TARGET_USER_USERHELLO_TXT, "rw");
                FileChannel fc = f.getChannel();
                MappedByteBuffer buf = fc.map(FileChannel.MapMode.READ_WRITE, 0, 20);
                
                buf.clear();
                buf.put("how are you?".getBytes());
                buf.force();
                LOGGER.info("Asker write \"how are you?\" 2 file buf {} remaining {} ", buf, buf.remaining());
                Thread.sleep(5000);
                
                
                //  buf.flip()方法不仅将position复位为0，同时也将limit的位置放置在了position之前所在的位置上
                // bug：所以只能读取 12个byte（因为"how are you?"只有12个byte，flip后limit只有12）
                // 改为重新映射文件 可以完整读取                
//                buf = fc.map(FileChannel.MapMode.READ_WRITE, 0, 20);
//                fc.read(buf);
                LOGGER.info("Asker read buf {}  remaining {} ", buf, buf.remaining());
                       
//                buf.flip();
               
                // 重读buffer所有数据                
                buf.rewind();
                LOGGER.info("Asker buf.rewind buf {}  remaining {} ", buf, buf.remaining());
                StringBuffer s = new StringBuffer();
                while (buf.hasRemaining())
                {
                    s.append((char) buf.get());
                }
                LOGGER.info("Asker receive: {}", s);
                fc.close();
                f.close();
                
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    }
    
    static class Answer implements Runnable
    {
        public void run()
        {
            
            RandomAccessFile f = null;
            try
            {
                Thread.sleep(1000);
                f = new RandomAccessFile(TARGET_USER_USERHELLO_TXT, "rw");
                FileChannel fc = f.getChannel();
                MappedByteBuffer buf = fc.map(FileChannel.MapMode.READ_WRITE, 0, 20);
                
                LOGGER.info("Answer read  buf {} remaining {}", buf, buf.remaining());
//                buf.flip();

                StringBuffer s = new StringBuffer();
                while (buf.hasRemaining())
                {
                    s.append((char) buf.get());
                }
                LOGGER.info("Answer receive: [{}] buf {}", s, buf);
                
                Thread.sleep(1000);
                
                buf.clear();
                LOGGER.info("Answer clear buf: {}", buf);
                buf.put("I am fine, thank you".getBytes());
                buf.force();
                LOGGER.info("write \"I am fine, thank you\" 2 file  buf {}", buf);
                fc.close();
                f.close();
                
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    }
    
    private static void createIfNoexist() throws IOException
    {
        try
        {
            Path path = Paths.get(TARGET_USER);
            System.out.println(path);
            System.out.println(Files.isDirectory(path));
            if(!Files.isDirectory(path))
            {
                Path createDirectories = Files.createDirectories(path);
                System.out.println(createDirectories.toAbsolutePath());
            }
//            果RandomAccessFile作为输出流时，写出到的文件如果不存在，则在执行过程中自动创建 如果写出到的文件存在，则会对原有文件内容进行覆盖。(默认情况下，从头覆盖)
            RandomAccessFile randomAccessFile = new RandomAccessFile(TARGET_USER_USERHELLO_TXT, "rw");
        }
        catch (Exception e)
        {
            LOGGER.error(e.getMessage(), e);
            /*
             * Files.write(Paths.get(TARGET_USER_USERHELLO_TXT), "".getBytes(),
             * StandardOpenOption.CREATE_NEW );
             */
        }
    }
}
